import axios from 'axios'
import qs from 'qs'
import store from '@/library/store'
import { createVNode } from 'vue' 
import { notification, message  } from 'ant-design-vue'

// 创建axios实例 axiso的一些基础参数配置,
const instance = axios.create({
  // 配置在config/prod.env里的baseApi
  baseURL: import.meta.env.VITE_HTTP,
  withCredentials: false,
  crossDomain: true,
  timeout: 5000, // 超时时间(ms)
  retry: 3, // 重新请求次数
  retryDelay: 1000 // 重新请求的间隙
})

/**
 *  请求拦截器
 */
instance.interceptors.request.use(
  req => {
    const { headers, method, params } = req
    // 设置授权的TOKEN
    headers.AuthorizationToken = store.state.auth.ACCESS_TOKEN
    // 判断为post请求，序列化传来的参数
    if (method === 'get') {
      //console.info('请求没有正文，但是也可以提交数据，提交的数据没有在正文中而是在URL中')
      if (params != undefined) {
        const urlparams = qs.stringify(params, {
          arrayFormat: 'repeat',
          allowDots: true
        })
        //console.dir(params)
        req.url = req.url.indexOf('?')> 0 ? req.url+'&' +urlparams : req.url +'?' +urlparams
        // 清空参数
        const keys = Object.keys(params)
        for (const key of keys) {
          delete params[key]
        }
      }
    } else if (method === 'post') {
      //console.info('向指定资源提交数据进行处理请求，数据被包含在请求体中，POST请求可能会导致新的资源的建立/已有资源的修改')
      req.data = qs.stringify(req.data, {
        arrayFormat: 'indices',
        allowDots: true
      })
    } else if (method === 'patch') {
      //console.info('是对PUT方法的补充,用来对已知资源进行局部更新') qs.stringify({ code: 'utf-8',  q: value })
      req.data = qs.stringify(req.data, {
        arrayFormat: 'repeat', // ids=1&ids=2
        allowDots: true
      })
    } else if (method === 'put') {
      //console.info('从客户端向服务端传送的数据取代指定的文档的内容')
      console.info('有JSON格式接收的时候使用本方法')
    } else if (method === 'delete') {
      console.info('请求服务器删除指定的页面')
    } else if (method === 'connect') {
      console.info('HTTP/1.1 协议中预留给能够连接改为管道方式的代理服务器')
    } else if (method === 'options') {
      console.info('允许客户端查看服务器的性能')
    } else if (method === 'trace') {
      console.info('回显服务器收到的请求，主要用于测试或诊断')
    } else if (method === 'head') {
      console.info('功能与GET类似。GET在服务器响应时，会有正文部分。而HEAD在服务器响应时，没有正文部分, 用于获取报头')
    }
    return req
  },
  error => {
    console.error('请求拦截异常:', error)
    return Promise.reject(error)
  }
)
/**
 * 响应拦截器
 */
instance.interceptors.response.use(
  res => {
    const { status, data, config, headers } = res
    console.info('HTTP RESPONSE DATA---------status: [', status,'] data:[', data, '] ')
    // 下载文件处理
    if(config.responseType == 'blob') {
      // 异常处理
      if(data.type == 'application/json'){
        const reader = new FileReader()
        reader.readAsText(data)
        reader.onload = function () {
          const err = JSON.parse(reader.result)
          message.error(err.message| '下载文件失败')
        }
        reader.onerror = function () {
          message.error('下载文件失败')
        }
        return Promise.reject(data)
      }
      //下载
      // // 切割出文件名
      // const fileNameEncode = res.headers['content-disposition'].split('filename=')[1]
      // // 解码
      // const fileName = decodeURIComponent(fileNameEncode)
      // console.log('fileName', fileName)
      let fileName
      if (headers['content-disposition']) {
        fileName = headers['content-disposition'].replace(/\w+;filename=(.*)/, '$1')
      } else {
        fileName = data.fileName
      }
      // 此处当返回json文件时需要先对data进行JSON.stringify处理，其他类型文件不用做处理
      const blob = new Blob([data], { type: headers['content-type'] })
      const dom = document.createElement('a')
      // 创建下载的链接
      const downUrl = window.URL.createObjectURL(blob) 
      dom.href = downUrl
      // 下载后文件名
      dom.download = decodeURIComponent(fileName)
      dom.style.display = 'none'
      document.body.appendChild(dom)
      dom.click()
      // 下载完成移除元素
      dom.parentNode.removeChild(dom)
      // 释放blob对象
      window.URL.revokeObjectURL(downUrl)
      return Promise.resolve(data)
    }
    const msg = data.msg||data.message
    if(data.code !=0){
      let liNodes = []
      if(data.code == 8888) {
        for(let o in data.data){
          liNodes.push(createLiNode(data.data[o]))
        }
      }else if(data.code == 3333){
        liNodes.push(createLiNode(data.data))
      }
      // 
      notification.warning({
        message: createVNode('strong', {style: {fontSize: '18px', color: 'red'}}, msg),
        description: liNodes.length> 0? createVNode('ul', { style: {padding: '0px', marginLeft: '-30px'}}, liNodes ) : ''
      })
    }
    // allowRequest()
    return (data.code == 0) ? Promise.resolve(data): Promise.reject(data)
  },
  err => {
    const { message, code, status } = err
    console.error('HTTP RESPONSE ERROR---------code:[', code ,'], status: [', status, '], message:', message)
    if (err && err.response) {
      switch (err.response.status) {
      case 400: err.message = '请求错误(400)' ; break
      case 401: err.message = '未授权，请重新登录(401)'; break
      case 403: err.message = '拒绝访问(403)'; break
      case 404: err.message = '请求出错(404)'; break
      case 408: err.message = '请求超时(408)'; break
      case 500: err.message = '服务器错误(500)'; break
      case 501: err.message = '服务未实现(501)'; break
      case 502: err.message = '网络错误(502)'; break
      case 503: err.message = '服务不可用(503)'; break
      case 504: err.message = '网络超时(504)'; break
      case 505: err.message = 'HTTP版本不受支持(505)'; break
      default: err.message = `连接出错(${err.response.status})!`
      }
      notification.error({
        message: err.message,
        description: '服务或网络出现异常, 代码 : ' + err.response.status ,
        style: {
          color: 'red'
        }
      })
    }else{
      err.message = '连接服务器失败!'
    }
    return Promise.reject(err.message)
  }
)

// 创建列表
const createLiNode =(msg)=>{
  return createVNode('li', {
    style: {
      fontSize: '16px', width: '100%',color: '#da4a16',
      listStyleType: 'circle'
    }
  } , msg)
}
export default instance

/**
 * HTTP 状态码分类
 * 1** 信息， 服务器收到请求，需要请求者继续执行操作
 * 2** 成功， 操作被成功接收并处理
 * 3** 重定向，需要进一步的操作以完成请求
 * 4** 客户端错误，请求包含语法错误或无法完成请求
 * 5** 服务器错误，服务器在处理请求的过程中发生了错误
 *
 */

/**
 * 头部
 * 头部：关于请求或响应，或者正文的一些描述字段。
 *
 * 组成：key: value\r\n
 * 典型头部字段：
 *
 *  Connection: 长短连接控制。keep-alive/close
 *   Referer: 记录本次请求的来源链接
 *   Content-Type: 用于表示正文的数据格式
 *   Content-Length: 用于表示正文的长度，http解决粘包问题的关键字段
 *   Location: 用于指定重定向的新链接地址，与3xx搭配使用
 *
 *
 * 空行

空行：\r\n。是与头部最后一个字段的结尾\r\n组成连续的\r\n\r\n作为http头部结尾的标志。
正文

http是一个应用层协议，只是应用程序如何沟通的一种数据格式约定，在传输层是基于tcp协议实现的。

http客户端实际就是一个tcp客户端，http服务器实际上就是一个tcp服务器，只不过http客户端与服务端的通信使用的是http协议来约定数据格式而已。
*/
